package com.chinaunicom.emergency.provider;

import com.alibaba.fastjson.JSONObject;
import com.chinaunicom.emergency.constant.ConstantUtil;
import com.chinaunicom.emergency.exception.OAuthException;
import com.chinaunicom.emergency.model.Lock;
import com.chinaunicom.emergency.model.Logs;
import com.chinaunicom.emergency.repository.LogsRepository;
import com.chinaunicom.emergency.security.RsaUtil;
import com.chinaunicom.emergency.service.RedisService;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author liliang
 * @date 2019-12-13
 */
@Log4j2
public class LoginAuthenticationProvider extends DaoAuthenticationProvider {

	@Autowired
    LogsRepository logsRepository;
    @Autowired
    RedisService redisService;
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Map param = (Map) authentication.getDetails();
        if(param.get("grant_type").equals("password")){
            Object ciphertext = param.get("ciphertext");
            String clientId = param.get("client_id").toString();
            if (ciphertext == null) {
                throw new OAuthException("ciphertext不能为空");
            }
            String temp = RsaUtil.decryptByPrivateKey(RsaUtil.getPrivateKeyStr(), (String) ciphertext);
            JSONObject json = null;
            try {
                json = JSONObject.parseObject(temp);
            } catch (Exception e) {
                throw new OAuthException("ciphertext解析失败");
            }
            String password = json.getString("password");
            String username = json.getString("userName");
            //验证码
            String code = json.getString("code");

            //用户名能为空,不记录错误次数
            if (StringUtils.isBlank(username)) {
                throw new OAuth2Exception("用户名不能为空，请检查");
            }
            //加锁处理
            Lock lock = new Lock(username+clientId,new Date().getTime()+"");
            Integer failTimes = 0;
            String failTimesKey = ConstantUtil.PREFIX_LOGIN_FAIL_COUNT+"_"+ "LOGINCOUNT_"+clientId;
            String field = username.toUpperCase()+"_"+clientId;

            try {
                boolean isLock = redisService.tryLock(lock);
                if(isLock) {

                    //根据当前登陆类型及用户信息获取失败登录次数
                    boolean hasFailTimesKey = redisService.hasKey(failTimesKey);
                    if(hasFailTimesKey){
                        Object redisCountValue = redisService.getHash(failTimesKey,field);
                        failTimes = (null != redisCountValue && !"{}".equals(redisCountValue.toString())) ? Integer.parseInt(redisCountValue.toString()) : 0;
                    }
                    //假设本次登录失败,失败次数累加
                    failTimes += 1;
                    //本次登录需要验证码,需判断验证码是否正确
                    boolean hasRedisCode = false;
                    if(failTimes > 4){
                        if (StringUtils.isNotBlank(code)) {
                            String resdisCode = "";
                            hasRedisCode = redisService.hasKey(ConstantUtil.PREFIX_CODE+"_"+clientId+"_imageCode_");
                            if(hasRedisCode){
                                Object redisCodeValue = redisService.getHash(ConstantUtil.PREFIX_CODE+"_"+clientId+"_imageCode_",username.toUpperCase());
                                if(null != redisCodeValue && !"{}".equals(redisCodeValue)){
                                    resdisCode = redisCodeValue.toString();
                                }
                            }
                            if(StringUtils.isBlank(resdisCode)){
                                //服务器验证码异常,不记录失败次数
                                throw new OAuth2Exception("验证码不存在,登录失败!");
                            }
                            //验证码不通过,校验失败,记录失败次数
                            if(!resdisCode.equals(code)){
                                redisService.putHash(failTimesKey,field,failTimes+"");
                                throw new OAuth2Exception("验证码错误,登录失败!");
                            }
                        }else{
                            //提示用户未输入验证码,记录失败次数
                            redisService.putHash(failTimesKey,field,failTimes+"");
                            throw new OAuth2Exception("验证码输入为空,登录失败!");
                        }
                    }
                    //密码为空,记录错误次数
                    if (StringUtils.isBlank(password)) {
                        redisService.putHash(failTimesKey,field,failTimes+"");
                         throw new OAuth2Exception("密码不能为空，请检查");
                    }



                    param.put("username",username);
                    param.put("password",password);
                    param.remove("ciphertext");
                    UsernamePasswordAuthenticationToken userPassToken = new UsernamePasswordAuthenticationToken(username,password);
                    userPassToken.setDetails(param);
                    Authentication au = super.authenticate(userPassToken);
                    Logs logs = new Logs();
                    logs.setLogDescribe("登录");
                    logs.setLogType("DL");
                    logs.setIsSoftDel(0);
                    logs.setCreateBy(username);
                    logs.setCreateTime(new Date());
                    logsRepository.save(logs);

                    if(hasFailTimesKey){
                        redisService.deleteHashFields(failTimesKey,field);
                    }
                    if(hasRedisCode) {
                        redisService.deleteHashFields(ConstantUtil.PREFIX_CODE + "_" + clientId + "_imageCode_", username.toUpperCase());
                    }

                    return au;
                }else {
                    //密码为空,记录错误次数
                    redisService.putHash(failTimesKey,field,failTimes+"");
                    throw new OAuth2Exception("登陆过快,请稍后再试!");
                }

            } catch (Exception e) {
                if(e instanceof OAuthException){
                    throw e;
                }else {
                    log.info(e instanceof UsernameNotFoundException);
                    log.info("认证失败：", e);
                    redisService.putHash(failTimesKey,field,failTimes+"");
                    //特殊处理,本次为第4次输入密码错误下一次需要验证码
                    if(failTimes >= 4){
                        Map map = new HashMap<String,String>();
                        OAuth2Exception oAuth2Exception = new OAuth2Exception("用户密码错误，请检查",e);
                        oAuth2Exception.addAdditionalInformation("needCode","");
                        throw oAuth2Exception;
                    }else{
                        throw new OAuth2Exception("用户密码错误，请检查");
                    }
                }
            }finally {
                //释放锁
                redisService.releaseLock(lock);
            }
        }
        return super.authenticate(authentication);

    }
}
